/*
 * comunicacion.c
 *
 *  Created on: 09/10/2010
 *      Author: teledhil
 */

#include <string.h>
#include <unistd.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>

#include "consola.h"
#include "comunicacion.h"

int crear_conexion() {

	int sock = 0;

	/*Creacion del socket*/
	if ((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0) {//AF_INET=PF_INET
		output_error("crear_conexion ha fallado");
		return -1;
	}

	return sock;
}

int conectar(int socket, char *servidor, int puerto) {
	struct sockaddr_in server; /*La direccion del servidor*/
	struct hostent *host_info;

	if ((host_info = gethostbyname(servidor)) == NULL) {
		output_error("conectar ha fallado. unknown host: %s", servidor);
		return -1;
	}

	/*Establecer direccion del socket del servidor y conectar*/
	server.sin_family = host_info->h_addrtype;
	memcpy((char *) &server.sin_addr, host_info->h_addr, host_info->h_length);
	server.sin_port = htons(puerto);

	if (connect(socket, (struct sockaddr *) &server, sizeof server) < 0) {
		//output_error("conectar ha fallado. connecting to server");
		return -1;
	}

	return 0;
}

int asociar(int socket, int puerto) {
	struct sockaddr_in server; /*La direccion del servidor*/
	socklen_t socketlen = sizeof(server);

	/*Establecer direccion del socket del servidor y conectar*/
	server.sin_family = AF_INET;
	server.sin_addr.s_addr = htonl(INADDR_ANY);
	server.sin_port = htons(puerto);

	if (bind(socket, (struct sockaddr *) &server, sizeof(server)) < 0) {
		output_error("aceptar: binding socket. Couldn't get port %d", puerto);
		return -1;
	}

	getsockname(socket, (struct sockaddr *) &server, &socketlen);
	output_debug("Socket asociado a puerto %d", ntohs(server.sin_port));

	if (listen(socket, 1) < 0) {
		output_error("aceptar: listen\n");
		return -1;
	}

	//Devuelve el puerto de escucha
	return ntohs(server.sin_port);
}

int esperar(int socket) {
	//struct sockaddr_in client;
	int fd;

	//socklen_t cliente_long;
	/*IMPORTANTE!!
	 * No debe quedarse bloqueada indefinidamente.
	 * Debe bloquearse solo por en breve periodo de tiempo*/

	fd_set sockets_lectura;
	struct timeval tiempoEspera;
	int retornoSelect;

	/*Añade el socket a la lista que select consultará*/
	FD_ZERO(&sockets_lectura);
	FD_SET(socket, &sockets_lectura);
	/*Tiempo que select estará bloqueado*/
	tiempoEspera.tv_sec = 1;
	tiempoEspera.tv_usec = 0;

	retornoSelect = select(socket + 1, &sockets_lectura, NULL, NULL,
			&tiempoEspera);

	retornoSelect = select(socket + 1, &sockets_lectura, NULL, NULL,
			&tiempoEspera);
	if (retornoSelect > 0) {
		if ((fd = accept(socket, NULL, NULL)) < 0) {
			output_error("aceptando la conexion\n");
			return -1;
		}
	} else if (retornoSelect == 0) {
		return 0;
	} else {
		output_error("Select");
		return -1;
	}

	return fd;
}

int enviar_mensaje(int socket, char *mensaje, size_t longitud) {

	long total_enviado = 0, enviado;

	//output_debug("enviar_mensaje: Envio mensaje %s por socket %d", mensaje, socket);
	//if (write(socket, mensaje, longitud) < 0) {
	while (total_enviado < ((long) longitud)) {
		if ((enviado = send(socket, (mensaje + total_enviado), (longitud
				- total_enviado), 0)) < 0) {
			output_error("enviar_mensaje ha fallado");
			return -1;
		}
		total_enviado += enviado;
		if (total_enviado < ((long) longitud)) {
			output_debug(
					"Otra iteracion para enviar el fragmento (llevo %d de %d)",
					total_enviado, longitud);
		}

	}

	return 0;
}

int recibir_mensaje(int socket, char *mensaje, size_t longitud) {

	/*IMPORTANTE!!
	 * No debe quedarse bloqueada indefinidamente.
	 * Debe bloquearse solo por en breve periodo de tiempo*/

	fd_set sockets_lectura;
	struct timeval tiempoEspera;
	int retornoSelect, leido;

	/*Añade el socket a la lista que select consultará*/
	FD_ZERO(&sockets_lectura);
	FD_SET(socket, &sockets_lectura);
	/*Tiempo que select estará bloqueado*/
	tiempoEspera.tv_sec = 1;
	tiempoEspera.tv_usec = 0;

	retornoSelect = select(socket + 1, &sockets_lectura, NULL, NULL,
			&tiempoEspera);

	if (retornoSelect > 0) {
		leido = read(socket, mensaje, longitud);
		if (leido > 0) {
			//output_debug("Recibo mensaje: |%c| del socket %d", *mensaje, socket);
			return leido;
		} else {
			return -1;
		}
	} else if (retornoSelect == 0) {
		return 0;
	} else {
		output_error("Select");
		return -1;
	}
}

int cerrar_conexion(int socket) {

	if (close(socket) < 0) {
		output_error("cerrar_conexion ha fallado");
		return -1;
	}

	return 0;
}
